package jmine.tec.rpc.common.lazy;

import java.io.Serializable;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

import jmine.tec.proxy.chain.ChainElement;
import jmine.tec.proxy.chain.InvocationChain;
import jmine.tec.rpc.common.entity.EntityRepresentation;
import bancosys.tec.persist.dao.BeanNotFoundException;

/**
 * ChainElement para representações lazy
 * 
 * @author lundberg
 */
public class LazyRepresenterChain implements ChainElement, Serializable {

    private final transient LazyRepresentationInitializer initializer;

    private static final Method GET_PRIMARY_KEY_METHOD;

    static {
        try {
            GET_PRIMARY_KEY_METHOD = EntityRepresentation.class.getDeclaredMethod("getPk");
        } catch (NoSuchMethodException e) {
            throw new ExceptionInInitializerError(e);
        }
    }

    /**
     * Construtor
     * 
     * @param initializer initializer
     */
    public LazyRepresenterChain(LazyRepresentationInitializer initializer) {
        this.initializer = initializer;
    }

    /**
     * {@inheritDoc}
     */
    public Object aroundInvoke(InvocationChain chain) throws Throwable {
        if (chain.getInvokingMethod().getName().equals("finalize") && chain.getParams().length == 0) {
            return null;
        } else if (chain.getInvokingMethod().getName().equals("toString") && chain.getParams().length == 0) {
            return this.initializer.toString();
        } else if (chain.getInvokingMethod().getName().equals("equals") && chain.getParams().length == 1
                && chain.getProxyObject() == chain.getParams()[0]) {
            return true;
        } else if (chain.getInvokingMethod().getName().equals("hashCode") && chain.getParams().length == 0) {
            return this.initializer.hashCode();
        } else if (this.isGetLazyRepresentationInitializer(chain)) {
            return this.initializer;
        } else if (chain.getInvokingMethod() == GET_PRIMARY_KEY_METHOD) {
            return this.initializer.getRepresentationId();
        } else if (chain.getInvokingMethod().getDeclaringClass().equals(LazyRepresentationInitializer.class)) {
            return chain.getInvokingMethod().invoke(this.initializer, chain.getParams());
        } else {
            return this.invokeOnRepresentation(chain);
        }
    }

    /**
     * Invoca o método na representação
     * 
     * @param chain chain
     * @return invocation result
     * @throws IllegalAccessException IllegalAccessException
     * @throws InvocationTargetException InvocationTargetException
     * @throws BeanNotFoundException BeanNotFoundException
     */
    private Object invokeOnRepresentation(InvocationChain chain) throws IllegalAccessException, InvocationTargetException,
            BeanNotFoundException {
        return chain.getInvokingMethod().invoke(this.initializer.getRepresentation(), chain.getParams());
    }

    /**
     * Verifica se o chain corresponde ao método da interface LazyRepresentation
     * 
     * @param chain chain
     * @return boolean
     * @throws NoSuchMethodException NoSuchMethodException
     */
    private boolean isGetLazyRepresentationInitializer(InvocationChain chain) throws NoSuchMethodException {
        return chain.getInvokingMethod().equals(LazyRepresentation.class.getMethod("getLazyRepresentationInitializer"));
    }
}
